package process.email;

import crawling.email.EmailCrawler;
import data.DocumentCollection;
import data.Seed;
import data.TermCollection;
import data.email.EmailDocumentCollection;
import data.email.EmailTermCollection;
import java.io.File;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import process.IndexGenerator;
import transform.email.EmailParser;
import transform.email.EmailStopper;

/**
 * EmailIndexGenerator adalah index generator untuk mengumpulkan term (beserta 
 * statistiknya) dari koleksi file email
 * @author Muqtafi Akhmad
 */
public class EmailIndexGenerator extends IndexGenerator {

    /**
     * jumlah worker
     */
    private Integer workerCount;
    /**
     * keep alive time worker untuk idle
     */
    private static Integer keepAliveTime = 0;
    /**
     * keep alive time unit 
     */
    private static TimeUnit keepAliveUnit = TimeUnit.SECONDS;
    /**
     * periode waktu untuk invokasi garbage collector, dalam milisecond
     */
    private static Integer periodTime = 60000;

    /**
     * konstruktor dari email index generator dengan parameter jumlah worker
     * @param workerCount jumlah document statistic reader
     */
    public EmailIndexGenerator(Integer workerCount) {
        this.workerCount = workerCount;
    }

    @Override
    public TermCollection doGenerateIndex(Seed rootSeed, ArrayList<String> seedFilter, boolean isCompressed) {
        File currDir = new File(".");
        File resultDir = new File(currDir.getAbsolutePath() + File.separator + _result_dir);
        if(!resultDir.exists()){
            resultDir.mkdir();
        }
        // create linguistic processors
        EmailStopper stopper = new EmailStopper();
        // create crawler and supply document for document collection
        EmailCrawler crawler = new EmailCrawler();
        // create term collection and document collection array
        ArrayList<DocumentCollection> documentCollections = new ArrayList<DocumentCollection>();
        // create worker
        ArrayBlockingQueue<Runnable> worker = new ArrayBlockingQueue<Runnable>(workerCount);
        ThreadPoolExecutor workerPool = new ThreadPoolExecutor(workerCount, workerCount, keepAliveTime, keepAliveUnit, worker);
        int iter;
        for (iter = 1; iter <= workerCount; iter++) {
            EmailDocumentStatisticsReader reader = new EmailDocumentStatisticsReader("Worker" + iter);
            TermCollection termCollection = new EmailTermCollection();
            // create document collection
            EmailDocumentCollection documentCollection = new EmailDocumentCollection();
            documentCollections.add(documentCollection);
            crawler.addDocument(documentCollection);
            reader.setDocumentCollection(documentCollection);
            reader.setTermCollection(termCollection);
            reader.setStopper(stopper);
            reader.setParser(new EmailParser(crawler));
            workerPool.execute(reader);
        }
        // prepare garbage collection invoker
        Timer gcTimer = new Timer();
        gcTimer.scheduleAtFixedRate(new GarbageCollectorInvoker(), 0, periodTime);
        System.out.println("## Start garbage collection invoker");
        // do crawl
        crawler.crawl(rootSeed, seedFilter);
        System.out.println("## Documents has been crawled, time to wait for the workers");
        // waiting for workers
        while (workerPool.getActiveCount() > 0) {
        }
        // stop garbage collection invoker
        gcTimer.cancel();
        System.out.println("## Terminate garbage collection invoker");
        TermCollection resultCollection = new EmailTermCollection();
        // write the document id mapping
        crawler.saveDocMappingFile(resultDir.getAbsolutePath(), isCompressed);
        resultCollection.setDocumentCount(crawler.getCrawledCount());
        return resultCollection;
    }

    /**
     * kelas GarbageCollectorInvoker bertugas untuk meng-invoke garbage collector
     * untuk membersihkan memori yang digunakan komputer
     */
    private static class GarbageCollectorInvoker extends TimerTask {

        public GarbageCollectorInvoker() {
        }

        /**
         * invoke garbage collector
         */
        @Override
        public void run() {
            System.out.println("## Activate garbage collector");
            System.gc();
        }
    }
}
